﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using LowLevelGraphics;
using LowLevelGraphics.Filter;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public enum MODE
    {
        HORIZONTAL,
        VERTICAL,
        BOTH
    }

    /// <summary>
    /// 
    /// </summary>
    public enum BOTH_MODE
    {
        AND,
        OR, 
        XOR
    }

    /// <summary>
    /// 
    /// </summary>
    public class VariationThreshold : BaseForeBackgroundColorFilter
    {
        public delegate int ColorDistanceDelegate(Color color1, Color color2);
        protected ColorDistanceDelegate m_ColorDistanceDelegate = null;
        protected int m_nVariationHorizontal = 500;
        protected int m_nVariationVertical = 500;
        protected MODE m_bHorizontal = MODE.BOTH;
        protected BOTH_MODE m_BothMode = BOTH_MODE.AND;
        /// <summary>
        /// 
        /// </summary>
        public VariationThreshold()
        {
            m_ColorDistanceDelegate = new ColorDistanceDelegate(ColorFunctions.DistanceSquared);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_variationThreshold"></param>
        internal VariationThreshold(VariationThreshold _variationThreshold)
        {
            this.m_nVariationVertical = _variationThreshold.m_nVariationVertical;
            this.m_nVariationHorizontal = _variationThreshold.m_nVariationHorizontal;
            this.m_bHorizontal = _variationThreshold.m_bHorizontal;
            this.m_BothMode = _variationThreshold.m_BothMode;
        }

        /// <summary>
        /// 
        /// </summary>
        public int Variation
        {
            set
            { 
                m_nVariationHorizontal = value; 
                m_nVariationVertical = value; 
            }
        }

        /// <summary>
        /// Gets or sets horizontal variation
        /// </summary>
        public int VariationHorizontal
        {
            get { return m_nVariationHorizontal; }
            set { m_nVariationHorizontal = value; }
        }

        /// <summary>
        /// Gets or sets vertical variation
        /// </summary>
        public int VariationVertical
        {
            get { return m_nVariationVertical; }
            set { m_nVariationVertical = value; }
        }

        /// <summary>
        /// Gets or sets Color Distance Delegate
        /// </summary>
        public ColorDistanceDelegate ColorDistance
        {
            get { return m_ColorDistanceDelegate; }
            set { m_ColorDistanceDelegate = value; }
        }

        /// <summary>
        /// Executes this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int x = 0;
            int y = 0;
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;

            Color color = Color.Transparent;
            Color colorPrevious = Color.Transparent;

            UnsafeBitmap bitmapMask = (UnsafeBitmap)_bitmap.Clone();

            switch (m_bHorizontal)
            {
                case MODE.HORIZONTAL:
                    Horizontal(_bitmap, bitmapMask);
                    break;

                case MODE.VERTICAL:
                    Vertical(_bitmap, bitmapMask);
                    break;

                case MODE.BOTH:
                    Vertical(_bitmap, bitmapMask);
                    Horizontal(_bitmap, bitmapMask);
                    break;
            }
            return _bitmap;
        }

        /// <summary>
        /// Horizontal
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_bitmapMask"></param>
        private void Horizontal(UnsafeBitmap _bitmap, UnsafeBitmap _bitmapMask)
        {
            Color color = Color.Transparent;
            Color colorPrevious = Color.Transparent;
            Color colorNew = Color.Transparent;
            Color colorMask = Color.Transparent;

            int x = 0;
            int y = 0;
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height; 
            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    colorPrevious = color;
                    color = _bitmap.GetPixel(x, y);
                    colorNew = m_ColorDistanceDelegate(colorPrevious, color) > m_nVariationHorizontal ? m_ColorBackground : m_ColorForeground;
                    colorMask = _bitmapMask.GetPixel(x, y);
                    if (m_bHorizontal == MODE.BOTH)
                    {
                        switch (m_BothMode)
                        {
                            case BOTH_MODE.AND:
                                colorNew = (colorMask == m_ColorForeground && colorNew == m_ColorForeground) ? m_ColorForeground : m_ColorBackground;
                                break;

                            case BOTH_MODE.OR:
                                colorNew = (colorMask == m_ColorForeground || colorNew == m_ColorForeground) ? m_ColorForeground : m_ColorBackground;
                                break;

                            case BOTH_MODE.XOR:
                                colorNew = ((colorMask == m_ColorForeground && colorNew != m_ColorForeground) || (colorMask != m_ColorForeground && colorNew == m_ColorForeground))? m_ColorForeground : m_ColorBackground;
                                break;
                        }
                    } _bitmap.SetPixel(x, y, colorNew);
                }
            }
        }

        /// <summary>
        /// Vertical
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_bitmapMask"></param>
        private void Vertical(UnsafeBitmap _bitmap, UnsafeBitmap _bitmapMask)
        {
            Color color = Color.Transparent;
            Color colorPrevious = Color.Transparent;
            Color colorNew = Color.Transparent;
            int x = 0;
            int y = 0;
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height; 
            for (x = 0; x < nWidth; x++)
            {
                for (y = 0; y < nHeight; y++)
                {
                    colorPrevious = color;
                    color = _bitmap.GetPixel(x, y);
                    colorNew = m_ColorDistanceDelegate(colorPrevious, color) > m_nVariationVertical ? m_ColorBackground : m_ColorForeground;         
                    _bitmapMask.SetPixel(x, y, colorNew);
                    _bitmap.SetPixel(x, y, colorNew);
                }
            }
        }

        /// <summary>
        /// clones this instance
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new VariationThreshold(this);
        }
    }
}
